package com.gmadmin.common;

import com.gmadmin.exception.BaseException;
import com.gmadmin.exception.SecurityException;
import com.gmadmin.exception.UserException;
import io.lettuce.core.RedisCommandTimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.xml.bind.ValidationException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author zhangliang
 * @version 1.0
 * @date 2021/2/7 12:56
 * 全局controller异常处理
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    protected static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理所有不可知的异常
     */
    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public ResponseEntity<ApiResponse<Object>> handleException(Throwable e){
        // 打印堆栈信息
        logger.error(getStackTrace(e));
        return buildResponseEntity(ApiResponse.ofMessage(e.getMessage()));
    }

    @ExceptionHandler(BaseException.class)
    @ResponseBody
    public ResponseEntity<ApiResponse<Object
            >> handleException(BaseException e){
        // 打印堆栈信息
        logger.error("运行时异常:{}",getStackTrace(e));
        return buildResponseEntity(ApiResponse.of(e.getCode(),e.getMessage(),e.getResult()));
    }


    /**
     * 处理自定义user异常
     */
    @ExceptionHandler(UserException.class)
    public ResponseEntity<ApiResponse<Object>> userException(UserException e){
        logger.error("用户异常返回:{}", e.getMessage());
        return buildResponseEntity(new ApiResponse<>().ofException(e));
    }

    @ExceptionHandler(InternalAuthenticationServiceException.class)
    public ResponseEntity<ApiResponse<Object>> internalAuthenticationServiceException(InternalAuthenticationServiceException e){
        logger.error("用户验证异常返回: {}",e.getMessage());
        return buildResponseEntity(ApiResponse.ofMessage(e.getMessage()));
    }

    /**
     * 处理自定security异常
     */
    @ExceptionHandler(SecurityException.class)
    public ResponseEntity<ApiResponse<Object>> baseException(SecurityException e){
        logger.error("安全验证异常:{}",e.getMessage());
        return buildResponseEntity(new ApiResponse<>().ofException(e));
    }

    /**
     * 统一返回异常
     */
    private ResponseEntity<ApiResponse<Object>> buildResponseEntity(ApiResponse<Object> apiError) {
        return new ResponseEntity<>(apiError, org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     * 获取堆栈信息
     */
    public static String getStackTrace(Throwable throwable){
        StringWriter sw = new StringWriter();
        try (PrintWriter pw = new PrintWriter(sw)) {
            throwable.printStackTrace(pw);
            return sw.toString();
        }
    }

    /**
     * 方法参数校验
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseEntity<ApiResponse<Object>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        return buildResponseEntity(ApiResponse.ofMessage(Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()));
    }

    /**
     * ValidationException
     */
    @ExceptionHandler(ValidationException.class)
    @ResponseBody
    public ResponseEntity<ApiResponse<Object>> handleValidationException(ValidationException e) {
        logger.error("ValidationException:{}", e.getMessage());
        return buildResponseEntity(ApiResponse.ofMessage(e.getCause().getMessage()));
    }


    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseBody
    public ResponseEntity<ApiResponse<Object>> handlerNoFoundException(NoHandlerFoundException e) {
        return buildResponseEntity(ApiResponse.ofStatus(HttpStatus.NOT_FOUND));
    }


    @ExceptionHandler(DuplicateKeyException.class)
    @ResponseBody
    public ResponseEntity<ApiResponse<Object>> handleDuplicateKeyException(DuplicateKeyException e) {
        String message=e.getCause().getMessage();
        message=setMessage(message);
        return buildResponseEntity(ApiResponse.ofMessage("'"+message+"'参数值已存在，请重新输入"));
    }

    /**
     * 根据异常字段名称设置返回消息
     * @param message
     * @return
     */
    String setMessage(String message){
        // 不能重复的属性
        String[] duplicateField={"sys_role.uniq_name","sys_user.username","sys_user.UK_kpubos9gc2cvtkb0thktkbkes"
        ,"sys_menu.uniq_name","sys_menu.uniq_title","sys_dict.sys_dict_name_uindex"};
        Map<String, String> duplicateFieldMap=new HashMap<>();
        duplicateFieldMap.put("sys_role.uniq_name","角色名");
        duplicateFieldMap.put("sys_user.username","用户名");
        duplicateFieldMap.put("sys_user.UK_kpubos9gc2cvtkb0thktkbkes","用户邮箱");
        duplicateFieldMap.put("sys_menu.uniq_name","菜单名");
        duplicateFieldMap.put("sys_menu.uniq_title","菜单标题");
        duplicateFieldMap.put("sys_dict.sys_dict_name_uindex","字典名称");

        String column="";

        for (String field:duplicateField
             ) {
             if (message.contains(field)){
                 column=duplicateFieldMap.get(field);
             }
        }
            return column;
    }
//    RedisConnectionFailureException


    @ExceptionHandler(RedisConnectionFailureException.class)
    @ResponseBody
    public ResponseEntity<ApiResponse<Object>> redisConnectionFailureException(RedisConnectionFailureException e) {
        logger.info("redis连接异常");
        return buildResponseEntity(ApiResponse.ofMessage("redis连接失败"));
    }

    @ExceptionHandler(RedisCommandTimeoutException.class)
    @ResponseBody
    public ResponseEntity<ApiResponse<Object>> redisCommandTimeoutException(RedisCommandTimeoutException e) {
        logger.info("redis连接超时异常");
        return buildResponseEntity(ApiResponse.ofStatus(HttpStatus.REDIS_TIME_OUT));
    }
}
